<?php

/**
 * @package mikrokosmos
 * mk draw library file created on  23-gen-2005
 * jaco_at_pixeldump_dot_org
 */


//------------------------------------------------------------------------
// "READ" FUNCTIONS
//------------------------------------------------------------------------

/**
 * reads data
 * return GRADIENTRECORD structure
 */
function read_gradientRecord_data($buf, $dsType = SHAPE_ID){

	$grd = array();

	$grd["ratio"] = ui8dec($buf[0]);
	$grd["gradientColor"] = read_color_data(substr($buf, 1), $dsType);

	return $grd;
}

/**
 * reads data
 * return GRADIENT structure
 */
function read_gradient_data($buf, $dsType = SHAPE_ID){

	$gd = array();
	$gd["gradientCount"] = ui8dec($buf[0]);
	$cLength = ($dsType == SHAPE_ID3) ? 4 : 3;
	$gd["gradientRecords"] = array();

	for($i = 0, $ofst = 1; $i < $gd["gradientCount"]; $i++, $ofst += $cLength)
		$gd["gradientRecords"][$i] = read_gradientRecord_data(substr($buf, $ofst, $cLength), $dsType);

	return $gd;
}


/**
 * reads data
 * returning FILLSTYLEARRAY fields in an associative array
 * as in the other read_[something] it MUST be used in a valid context
 * e.g. reading defineshape tag
 */
function read_fillStyleArray_data($buf, $dsType = SHAPE_ID){

	$fStyleAr = array();
	$fStyleAr["fillStyles"] = array ();
	$ofst = 1;
	$fStyle["fillStyleArrayLength"] = $ofst;
	$fStyleAr["fillStyleCount"] = ui8dec($buf[0]);

	if(!$fStyleAr["fillStyleCount"])
		return $fStyleAr; // this fillstylearray does not contains fillstyles

	if($fStyleAr["fillStyleCount"] == 0xFF){
		$fStyleAr["fillStyleCount"] = ui16dec(substr($buf, $ofst, 2));
		$ofst += 2;
	}

	for($i = 0; $i < $fStyleAr["fillStyleCount"]; $i++){

		$fStyleAr["fillStyles"][] = read_fillStyle_data(substr($buf, $ofst), $dsType);
		$fsIndex = count($fStyleAr["fillStyles"]) - 1;
		$ofst += $fStyleAr["fillStyles"][$fsIndex]["fillStyleLength"];
	}

	$fStyleAr["fillStyleArrayLength"] = $ofst;

	return $fStyleAr;
}

/**
 * read data
 * returning an associative array cointaining FILLSTYLE fields
 * take care to use as in other read_[something]
 */
function read_fillStyle_data($buf, $dsType = SHAPE_ID){

	$fStyle = array();
	$ofst = 0;

	$fStyle["fillStyleType"] = ui8dec($buf[0]);
	$fStyle["fillStyleLength"] = 0;
	$ofst++;

	if($fStyle["fillStyleType"] == SOLID_FILL){

		$fStyle["fillColor"] = array();
		$fStyle["fillColor"]["red"] = ui8dec($buf[$ofst++]);
		$fStyle["fillColor"]["green"] = ui8dec($buf[$ofst++]);
		$fStyle["fillColor"]["blue"] = ui8dec($buf[$ofst++]);

		if(	$dsType == SHAPE_ID2 ||
			$dsType == SHAPE_ID3 ||
			$dsType == SHAPE2 ||
			$dsType == SHAPE3){ // this is in case of defineshape2 or 3

			$fStyle["fillColor"]["alpha"] = ui8dec($buf[$ofst++]);
		}

		$fStyle["fillStyleLength"] = $ofst;
	}
	else if($fStyle["fillStyleType"] == LINEAR_GRADIENT_FILL ||
			$fStyle["fillStyleType"] == RADIAL_GRADIENT_FILL){

		$fStyle["gradientMatrix"] = array();
		$fStyle["gradientMatrix"] = read_matrix_data(substr($buf, $ofst));

		$ofst += $fStyle["gradientMatrix"]["matrixLength"];

		$fStyle["gradient"] = read_gradient_data(substr($buf, $ofst)); // IMPLEMENT THIS!!!!!!
		$fStyle["fillStyleLength"] = $ofst + $fStyle["gradientLength"];
	}
	else if($fStyle["fillStyleType"] == REPEATING_BITMAP_FILL ||
			$fStyle["fillStyleType"] == CLIPPED_BITMAP_FILL ||
			$fStyle["fillStyleType"] == NS_BITMAP_FILL ||
			$fStyle["fillStyleType"] == NS_CLIPPED_BITMAP_FILL){

		$fStyle["bitmapID"] = ui16dec(substr($buf, $ofst, 2));
		$ofst += 2;

		$fStyle["bitmapMatrix"] = read_matrix_data(substr($buf, $ofst));
		$fStyle["fillStyleLength"] = $ofst + $fStyle["bitmapMatrix"]["matrixLength"];
	}

	return $fStyle;
}

/**
 * reads data
 * returning LINESTYLEARRAY fields in an associative array as in the other read_
 * [something] it MUST be used in a valid context e.g. reading defineshape tag
 */
function read_lineStyleArray_data($buf, $dsType = SHAPE_ID){

	$lStyleAr = array();
	$lStyleAr["lineStyles"] = array ();
	$ofst = 1;
	$lStyleAr["lineStyleArrayLength"] = $ofst;

	$lStyleAr["lineStyleCount"] = ui8dec($buf[0]);

	if(!$lStyleAr["lineStyleCount"])
		return $lStyleAr; // this fillstylearray does not contains fillstyles

	if($lStyleAr["lineStyleCount"] == 0xFF){
		$lStyleAr["lineStyleCount"] = ui16dec(substr($buf, $ofst, 2));
		$ofst += 2;
	}


	for($i = 0; $i < $lStyleAr["lineStyleCount"]; $i++){

		$lStyleAr["lineStyles"][] = read_lineStyle_data(substr($buf, $ofst), $dsType);
		$lsIndex = count($lStyleAr["lineStyles"]) - 1;
		$ofst += $lStyleAr["lineStyles"][$lsIndex]["lineStyleLength"];
	}

	$lStyleAr["lineStyleArrayLength"] = $ofst;

	return $lStyleAr;
}

/**
 * read data
 * returning an associative array cointaining LINESTYLE fields take care to use
 * as in other read_[something]
 */
function read_lineStyle_data($buf, $dsType = SHAPE_ID){

	$lStyle = array();
	$ofst = 0;

	$lStyle["lineWidth"] = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;

	$lStyle["lineColor"] = array();
	$lStyle["lineColor"]["red"] = ui8dec($buf[$ofst++]);
	$lStyle["lineColor"]["green"] = ui8dec($buf[$ofst++]);
	$lStyle["lineColor"]["blue"] = ui8dec($buf[$ofst++]);
	$lStyle["lineStyleLength"] = 0;

	if(	$dsType == SHAPE_ID2 ||
		$dsType == SHAPE_ID3 ||
		$dsType == SHAPE2 ||
		$dsType == SHAPE3){ // this is in case of defineshape2 or 3

		$lStyle["lineColor"]["alpha"] = ui8dec($buf[$ofst++]);
	}

	$lStyle["lineStyleLength"] = $ofst;

	return $lStyle;
}

/**
 * reads a byte
 * return nBits neded for indexing selection in FILLSTYLEARRAY
 */
function read_fillStyle_nBits($b){	return bindec(substr(get_binData_string($b), 0, 4)); }

/**
 * reads a byte
 * return nBits neded for indexing selection in LINESTYLEARRAY
 */
function read_lineStyle_nBits($b){	return bindec(substr(get_binData_string($b), 4)); }


 /**
 * verifies if a given byte can be read as STYLECHANGERECORD
 * note that it MUST be used in a valid context
 * that is parsing SHAPERECORD
 * specifying boolFlag returns symbolic constants
 */
function is_stylechange_record($b, $boolFlag = true){

	$ch = (strlen($b) > 1) ? $b[0] : $b;
	$chStr = swf_value2bin(ui8dec($ch), 8);
	$typeFlag = $chStr[0];

	if($typeFlag == "1") return ($boolFlag) ? false : IS_EDGERECORD;

	$stateBits = substr($chStr, 1, 5);
	if($typeFlag == "00000") return ($boolFlag) ? false : IS_ENDSHAPE_RECORD;

	return ($boolFlag) ? true :IS_STYLECHANGERECORD;
}

/**
 * verifies if a given byte can be read as ENDSHAPERECORD
 * note that it MUST be used in a valid context
 * that is parsing SHAPERECORD
 * specifying boolFlag returns symbolic constants
 */
function is_end_shape_record($b, $boolFlag = true){

	$bLength = strlen($b);
	$ch = $b[0];

	if($bLength >= 6){

		for($i = 0; $i < 6; $i++){
			if(strpos($b[$i], "10") === false) break;
		}

		if($i > 6){

			$chStr = substr($b, 0, 6);

			if($chStr == "000000")
				return ($boolFlag) ? true : IS_END_SHAPERECORD;
			else return ($boolFlag) ? true : 0;
		}
	}

	$chStr = swf_value2bin(ui8dec($ch), 8);

	$esr = substr($chStr, 0 , 6);

	if($esr == "000000")
		return ($boolFlag) ? true : IS_END_SHAPERECORD;
	else return ($boolFlag) ? true : 0;
}

/**
 * verifies if a given byte can be read as EDGERECORD
 * note that it MUST be used in a valid context
 * that is parsing SHAPERECORD
 * specifying boolFlag returns symbolic constants
 */
function is_edge_record($b, $boolFlag){

	$bLength = strlen($b);
	$ch = $b[0];

	if($bLength >= 2){

		if(strpos($b[0], "10") === false &&
			strpos($b[1], "10") == false) break;
		else {
			if($b[0] == "1")
				return ($boolFlag) ? true : IS_EDGERECORD;
			else return ($boolFlag) ? false : NOT_EDGERECORD;
		}
	}

	$chStr = swf_value2bin(ui8dec($ch), 8);
	if($chStr[0] == "1")
		return ($boolFlag) ? true : IS_EDGERECORD;
	else return ($boolFlag) ? false : NOT_EDGERECORD;
}


 /**
 * reads data
 * returns type of edge record
 */
function parse_shape_record($b){

	$ch = $b[0];
	$bLength = strlen($b);
	$flag = 0;

	if($bLength >= 6){

		if(substr($b, 0, 6) == "000000") return IS_END_SHAPERECORD;

		if($b[0] == "0")
			$flag = $flag | IS_STYLECHANGERECORD | IS_NONEDGERECORD;
		else {

			$flag = $flag | IS_EDGERECORD;

			if($b[1] == "0") $flag = $flag | IS_CURVED_EDGERECORD;
			else $flag = $flag | IS_STRAIGHT_EDGERECORD;
		}

		return $flag;
	}

	$chStr = swf_value2bin(ui8dec($ch), 8);

	if(substr($chStr, 0, 6) == "000000") return IS_END_SHAPERECORD;

	if($chStr[0] == "0")
		$flag = $flag | IS_STYLECHANGERECORD | IS_NONEDGERECORD;
	else $flag = $flag | IS_EDGERECORD;

	if(substr($chStr, 0, 2) == "11") $flag = $flag | IS_STRAIGHT_EDGERECORD;
	if(substr($chStr, 0, 2) == "10") $flag = $flag | IS_CURVED_EDGERECORD;

	return $flag;
}

 /**
 * reads data
 * returns an associative array containing STYLECHANGERECORD fields
 */
function read_stylechange_record_data($buf, $fBits = 0, $lBits = 0, $offset = 0, $dsType = SHAPE_ID){

	$bufStr = get_binData_string($buf);
	$bufStr = substr($bufStr, $offset); // strip unused bits

	$scr = array();
	$ofst = 1;

	$scr["typeFlag"] = 0;
	$scr["stateNewStyles"] = (int) $bufStr[$ofst++];
	$scr["stateLineStyle"] = (int) $bufStr[$ofst++];
	$scr["stateFillStyle1"] = (int) $bufStr[$ofst++];
	$scr["stateFillStyle0"] = (int) $bufStr[$ofst++];
	$scr["stateMoveTo"] = (int) $bufStr[$ofst++];

	$scr["fillStyle0"] = -1; //this means "no set", 0 means "set but no draw"
	$scr["fillStyle1"] = -1;
	$scr["lineStyle"] = -1;

	if($scr["stateMoveTo"]){
		$scr["nBitsMoveTo"] = bindec(substr($bufStr, $ofst, 5));
		$ofst += 5;
		$scr["moveX"] = bin2dec(substr($bufStr, $ofst, $scr["nBitsMoveTo"]));
		$ofst += $scr["nBitsMoveTo"];
		$scr["moveY"] = bin2dec(substr($bufStr, $ofst, $scr["nBitsMoveTo"]));
		$ofst += $scr["nBitsMoveTo"];
	}

	if($scr["stateFillStyle0"] && $fBits >= 0){
		$scr["fillStyle0"] = bindec(substr($bufStr, $ofst, $fBits));
		$ofst += $fBits;
	}
	if($scr["stateFillStyle1"] && $fBits >= 0){
		$scr["fillStyle1"] = bindec(substr($bufStr, $ofst, $fBits));
		$ofst += $fBits;
	}
	if($scr["stateLineStyle"] && $lBits >= 0){
		$scr["lineStyle"] = bindec(substr($bufStr, $ofst, $lBits));
		$ofst += $lBits;
	}
	if($scr["stateNewStyles"]){ // BE CAREFUL, THIS SECTION IS NOT COMPLETE!!!!!

		$ofst += strlen(get_pad($ofst + $offset));

		$scr["fillStyleArray"] = array();
		$scr["fillStyleArray"] = read_fillStyleArray_data(substr($buf, ($ofst + $offset) / 8), $dsType);
		$ofst += $scr["fillStyleArray"]["fillStyleArrayLength"] * 8;

		$scr["lineStyleArray"] = array();
		$scr["lineStyleArray"] = read_lineStyleArray_data(substr($buf, ($ofst + $offset) /8), $dsType);
		$ofst += $scr["lineStyleArray"]["lineStyleArrayLength"] * 8;

		$scr["nFillBits"] = bindec(substr($bufStr, $ofst, 4));
		$ofst += 4;
		$scr["nLineBits"] = bindec(substr($bufStr, $ofst, 4));
		$ofst += 4;
	}

	$scr["nBitsStyleChange"] = $ofst;
	$scr["binStr"] = substr($bufStr, 0, $ofst);

	return $scr;
}

/**
 * reads data
 * returns an associative array containing STRAIGHTEDGERECORD fields
 */
function read_straight_edgerecord_data($buf, $offset = 0){

	$bufStr = get_binData_string($buf);
	$bufStr = substr($bufStr, $offset); // strip unused bits
	$ser = array();
	$ofst = 2;

	$ser["typeFlag"] = 1;
	$ser["straightFlag"] = 1;
	$ser["nBits"] = bindec(substr($bufStr, $ofst, 4)) + 2;
	$ofst += 4;
	$ser["generalLineFlag"] = (int) $bufStr[$ofst++];

	if($ser["generalLineFlag"]){
		$ser["deltaX"] = bin2dec(substr($bufStr, $ofst, $ser["nBits"]));
		$ofst += $ser["nBits"];
		$ser["deltaY"] = bin2dec(substr($bufStr, $ofst, $ser["nBits"]));
		$ofst += $ser["nBits"];

		$ser["nBitsStraightEdgeRecord"] = $ofst;
		$ser["binStr"] = substr($bufStr, 0, $ofst);

		return $ser;
	}

	$ser["verticalFlag"] = (int) $bufStr[$ofst++];
	if($ser["verticalFlag"])
		$ser["deltaY"] = bin2dec(substr($bufStr, $ofst, $ser["nBits"]));
	else
		$ser["deltaX"] = bin2dec(substr($bufStr, $ofst, $ser["nBits"]));

	$ofst += $ser["nBits"];
	$ser["nBitsStraightEdgeRecord"] = $ofst;
	$ser["binStr"] = substr($bufStr, 0, $ofst);

	return $ser;
}

/**
 * reads data
 * returns an associative array containing CURVEDEDGERECORD fields
 */
function read_curved_edgerecord_data($buf, $offset = 0){

	$bufStr = get_binData_string($buf);
	$bufStr = substr($bufStr, $offset); // strip unused bits
	$cer = array();
	$ofst = 2;

	$cer["typeFlag"] = 1;
	$cer["straightFlag"] = 0;
	$cer["nBits"] = bindec(substr($bufStr, $ofst, 4)) + 2;
	$ofst += 4;
	$cer["controlDeltaX"] = bin2dec(substr($bufStr, $ofst, $cer["nBits"]));
	$ofst += $cer["nBits"];
	$cer["controlDeltaY"] = bin2dec(substr($bufStr, $ofst, $cer["nBits"]));
	$ofst += $cer["nBits"];
	$cer["anchorDeltaX"] = bin2dec(substr($bufStr, $ofst, $cer["nBits"]));
	$ofst += $cer["nBits"];
	$cer["anchorDeltaY"] = bin2dec(substr($bufStr, $ofst, $cer["nBits"]));
	$ofst += $cer["nBits"];
	$cer["nBitsCurvedEdgeRecord"] = $ofst;

	$cer["binStr"] = substr($bufStr, 0, $ofst);
	return $cer;
}


//------------------------------------------------------------------------
// "WRITE" FUNCTIONS
//------------------------------------------------------------------------

/**
 * write GRADIENT structure from a given gradient array
 */
function write_gradient_data($grd){

	$buf = "";
	$buf .= isset($grd["gradientCount"]) ? chr($grd["gradientCount"]) : chr(count($grd["gradientRecords"]));

	foreach($grd["gradientRecords"] as $gr) $buf .= write_gradientRecord_data($gr);

	return $buf;
}

/**
 * write GRADIENTRECORD structure
 * from a given gradientrecord array containing these fields:
 * ratio
 * gradientColor: red, green, blue, [alpha]
 */
function write_gradientRecord_data($gr){

	$buf = "";

	$buf .= chr($gr["ratio"]);
	$c = $gr["gradientColor"];
	$buf .= write_color_data($c);

	return $buf;
}



/**
 * write FILLSTYLEARRAY data from a given fillStyle array
 */
function write_fillStyleArray_data($fsad, $inTwips = true){

	$buf = "";

	if(!isset($fsad["fillStyleCount"])) $fsCount = count($fsad);
	else $fsCount = $fsad["fillStyleCount"];

	if($fsCount > 0xFF) $buf .= chr(0xFF) .swf_word($fsCount);
	else $buf .= chr($fsCount);

	foreach($fsad["fillStyles"] as $fStyle)
		$buf .= write_fillStyle_data($fStyle, $inTwips);

	return $buf;
}


/**
 * write FILLSTYLE data from a given fillStyle
 */
function write_fillStyle_data($fsd, $inTwips = true){

	$buf = "";

	$buf .= chr($fsd["fillStyleType"]);

	if($fsd["fillStyleType"] == SOLID_FILL){
		$buf .= write_color_data($fsd["fillColor"]);
	}
	else if($fsd["fillStyleType"] == LINEAR_GRADIENT_FILL ||
		$fsd["fillStyleType"] == RADIAL_GRADIENT_FILL){

		$buf .= write_matrix_data($fsd["gradientMatrix"]);
		$buf .= write_gradient_data($fsd["gradient"]);
	}
	else if($fsd["fillStyleType"] == REPEATING_BITMAP_FILL ||
		$fsd["fillStyleType"] == CLIPPED_BITMAP_FILL ||
		$fsd["fillStyleType"] == NS_BITMAP_FILL ||
		$fsd["fillStyleType"] == NS_CLIPPED_BITMAP_FILL){

		$buf .= swf_word($fsd["bitmapID"]);
		$buf .= write_matrix_data($fsd["bitmapMatrix"]);
	}

	return $buf;
}


/**
 * write LINESTYLEARRAY data from a given lineStyle array
 */
function write_lineStyleArray_data($lsad, $inTwips = true){

	$buf = "";

	if($lsad["lineStyleCount"] > 0xFF) $buf .= chr(0xFF) .swf_word($lsad["lineStyleCount"]);
	else $buf .= chr($lsad["lineStyleCount"]);

	foreach($lsad["lineStyles"] as $lStyle)
		$buf .= write_lineStyle_data($lStyle, $inTwips);

	return $buf;
}

/**
 * write LINESTYLE data from a given array
 * containing these fields:
 * lineWidth,
 * linecolor: red, green, blue, [alpha] -> optional
 */
function write_lineStyle_data($lStyle, $inTwips = true){

	$buf = "";

	$lw = ($inTwips) ? $lStyle["lineWidth"] : nmb2twip($lStyle["lineWidth"]);
	$buf .= swf_word($lw);
	$c = $lStyle["lineColor"];

	$buf .= write_color_data($c);

	return $buf;
}


/**
 * return nBits neded for indexing selection in FILLSTYLEARRAY
 */
function write_fillStyle_nBits($fillCount){

	$fbIndex = boundBits($fillCount);
	if(!$fillCount) $fbIndex = 0;

	return swf_value2bin($fbIndex, 4);
}


/**
 * return nBits neded for indexing selection in LINESTYLEARRAY
 */
function write_lineStyle_nBits($lineCount){

	$lbIndex = boundBits($lineCount);
	if(!$lineCount) $lbIndex = 0;

	return swf_value2bin($lbIndex, 4);
}




/**
 * compose a byte containing nFillBits and nLineBits
 * return a byte or binStr depending on binStr flag
 */
function write_fill_line_indexes($fillCount = 0, $lineCount = 0, $binStr = true){

	$bufStr = "";
	//$fbIndex = boundBits($fillcount);
	//$lbIndex = boundBits($lineCount);
	//if(!$fillCount) $fbIndex = 0;
	//if(!$lineCount) $lbIndex = 0;

	//$bufStr .= swf_value2bin($fbIndex, 4);
	//$bufStr .= swf_value2bin($lbIndex, 4);

	$bufStr .= write_fillStyle_nBits($fillCount);
	$bufStr .= write_lineStyle_nBits($lineCount);

	return ($binStr) ? $bufStr : chr(bindec($bufStr));
}

/**
 * compose a binStr
 * @params: moveTo, fsData, lsData
 * moveTo contains: x, y
 * fsData contains: fillBits, fsIndex1, fsIndex0
 * lsData contains: lineBits, lsIndex
 */
function write_stylechange_record_binStr($moveTo = array(), $fsData = array(), $lsData = array()){

	$bufStr = "00";

	$bufStr .= (count($lsData) && $lsData["lineBits"]) ? "1" : "0";

	if(count($fsData) && $fsData["fillBits"]){

	 $bufStr .= $fsData["fsIndex1"] ? "1" : "0";
	 $bufStr .= $fsData["fsIndex0"] ? "1" : "0";
	}
	else $bufStr .= "00";

	if(count($moveTo)) {

		$bufStr .= "1";
		$nBits = find_minBits(array($moveTo["x"], $moveTo["y"]));
		$bufStr .= swf_value2bin($nBits, 5);
		$bufStr .= swf_value2bin($moveTo["x"], $nBits);
		$bufStr .= swf_value2bin($moveTo["y"], $nBits);
	}
	else $bufStr .= "0";

	if(count($fsData) && $fsData["fillBits"]){
		if($fsData["fsIndex0"] >= 0) $bufStr .= swf_value2bin($fsData["fsIndex0"], $fsData["fillBits"]);
		if($fsData["fsIndex1"] >= 0) $bufStr .= swf_value2bin($fsData["fsIndex1"], $fsData["fillBits"]);
	}
	if(count($lsData) && $lsData["lineBits"])
		$bufStr .= swf_value2bin($lsData["lsIndex"], $lsData["lineBits"]);

	return $bufStr;
}

/**
 * returns a binstr from a given associative arrays
 */
function write_stylechange_record_data($scr, $includeStyles = true, $offset = 0){

	$binStr = "0";

	$binStr .= $scr["stateNewStyles"] ? "1" : "0";
	$binStr .= $scr["stateLineStyle"] ? "1" : "0";
	$binStr .= $scr["stateFillStyle1"] ? "1" : "0";
	$binStr .= $scr["stateFillStyle0"] ? "1" : "0";
	$binStr .= $scr["stateMoveTo"] ? "1" : "0";

	if($scr["stateMoveTo"]) {

		$binStr .= swf_value2bin($scr["nBitsMoveTo"], 5);
		$binStr .= swf_value2bin($scr["moveX"], $scr["nBitsMoveTo"]);
		$binStr .= swf_value2bin($scr["moveY"], $scr["nBitsMoveTo"]);
	}

	if($scr["stateFillStyle0"])
		$binStr .=  swf_value2bin($scr["fillStyle0"], $scr["fsBits"]);

	if($scr["stateFillStyle1"])
			$binStr .=  swf_value2bin($scr["fillStyle1"], $scr["fsBits"]);

	if($scr["stateLineStyle"] && $scr["lsBits"])
		$binStr .=  swf_value2bin($scr["lineStyle"], $scr["lsBits"]);

	if($scr["stateNewStyles"] && $includeStyles){

		$binStr .= get_binData_string(write_fillStyle_array_data($scr["fillStyleArray"]));
		$binStr .= get_binData_string(write_lineStyle_array_data($scr["lineStyleArray"]));
		$binStr .= swf_value2bin($scr["nLineBits"], 4);
	}

	return $binStr;
}

/**
 * compose a STRAIGHTEDGERECORD binStr
 * from a given array containing these fields
 * deltaX, deltaY both optional,
 * at least one is required
 */
function write_straight_edgerecord_binStr($ser){

	$binStr = "11"; // edge and straight record

	$dx = $ser["deltaX"];
	$dy = $ser["deltaY"];

	$nBits = find_minBits(array($dx, $dy)) - 2;
	$binStr .= swf_value2bin($nBits, 4);

	$generalLineFlag = ($ser["deltaX"] && $ser["deltaY"]) ? "1" : "0";

	if($generalLineFlag){ // line is not orthogonal
		$binStr .= "1";
		$binStr .= swf_value2bin($dx, $nBits + 2);
		$binStr .= swf_value2bin($dy, $nBits + 2);
	}
	else { // line is orthogonal

		$binStr .= "0";
		if($ser["deltaY"]){ // line is vertical
			$binStr .= "1";
			$binStr .= swf_value2bin($dy, $nBits + 2);
		}
		else { // line is horizontal
			$binStr .= "0";
			$binStr .= swf_value2bin($dx, $nBits + 2);
			//$binStr .= substr(dec2bin($dx), 0, $nBits + 2);
		}
	}

	return $binStr;
}


/**
 * compose a CURVEDDGERECORD binStr
 * from a given array containing these fields
 * controlX, controlY, anchorX, anchorY
 * all are required
 */
function write_curved_edgerecord_binStr($cer){

	$binStr = "10"; // edge and curved record

	$cx = $cer["controlDeltaX"];
	$cy = $cer["controlDeltaY"];
	$ax = $cer["anchorDeltaX"];
	$ay = $cer["anchorDeltaY"];

	$nBits = find_minBits(array($cx, $cy, $ax, $ay)) - 2;

	$binStr .= swf_value2bin($nBits, 4);
	$binStr .= swf_value2bin($cx, $nBits + 2);
	$binStr .= swf_value2bin($cy, $nBits + 2);
	$binStr .= swf_value2bin($ax, $nBits + 2);
	$binStr .= swf_value2bin($ay, $nBits + 2);

	return $binStr;
}


/**
 * wrapper to stylechangerecord
 * opt is an optional array containing these fields:
 * fBits, $lBits, $fsIndex1, $fsIndex0, $lsIndex
 * intTwips flag is used for twips conversione
 */
function set_lineStyle($lineBits, $lsIndex){

	$lsData = array();
	$lsData["lineBits"] = $lineBits;
	$lsData["lsIndex"] = $lsIndex;

	return write_stylechange_record_binStr(array(), array(), $lsData);
}

/**
 * wrapper to stylechangerecord
 * opt is an optional array containing these fields:
 * fBits, $lBits, $fsIndex1, $fsIndex0, $lsIndex
 * intTwips flag is used for twips conversione
 */
function moveTo($x, $y, $opt = array(), $inTwips = false){

	$mt = array();
	$mt["x"] = ($inTwips) ? $x : nmb2twip($x);
	$mt["y"] = ($inTwips) ? $y : nmb2twip($y);

	// set defaults use
	// note that in fsIndex and lsIndex
	// -1 means: not to use (don't store)
	// 0  means : "no draw" (can be stored)
	$lsData = array();
	$lsData["lineBits"] = 0;
	$lsData["lsIndex"] = -1;

	$fsData = array();
	$fsData["fillBits"] = 0;
	$fsData["fsIndex1"] = -1;
	$fsData["fsIndex0"] = -1;

	if(count($opt)){ // there are optional data

		if(isset($opt["lineBits"])){
			$lsData["lineBits"] = $opt["lineBits"];
			$lsData["lsIndex"] = $opt["lsIndex"];
		}

		if(isset($opt["fillBits"])){
			$fsData["fillBits"] = $opt["fillBits"];

			if(isset($opt["fsIndex1"]))
				$fsData["fsIndex1"] = $opt["fsIndex1"];

			if(isset($opt["fsIndex0"]))
				$fsData["fsIndex0"] = $opt["fsIndex0"];
		}
	}

	return write_stylechange_record_binStr($mt, $fsData, $lsData);
}


/**
 * wrapper to straightedgerecord
 * passing x or y as NULL use hor/vert flag
 * intTwips flag is used for twips conversione
 */
function lineTo($x = null, $y = null, $inTwips = false){

	$ser = array();
	if(!is_null($x)) $ser["deltaX"] = ($inTwips) ? $x : nmb2twip($x);
	if(!is_null($y)) $ser["deltaY"] = ($inTwips) ? $y : nmb2twip($y);

	return write_straight_edgerecord_binStr($ser);
}

/**
 * wrapper to curvedegderecord
 * all parameters but intTwips are required
 * intTwips flag is used for twips conversione
 */
function curveTo($ax, $ay, $cx, $cy, $inTwips = false){

	$cer = array();
	$cer["anchorX"] = ($inTwips) ? $ax : nmb2twip($ax);
	$cer["anchorY"] = ($inTwips) ? $ay : nmb2twip($ay);
	$cer["controlX"] = ($inTwips) ? $cx : nmb2twip($cx);
	$cer["coltrolY"] = ($inTwips) ? $cy : nmb2twip($cy);

	return write_curvededge_record_binStr($cer);
}


//------------------------------------------------------------------------
// "MATH" FUNCTIONS
// supporting bezier 3rd order spline drawing
// many thanks to mr. Penner www.robertpenner.com
//------------------------------------------------------------------------

function intersect2Lines ($p1, $p2, $p3, $p4) {

	$x1 = $p1["x"]; $y1 = $p1["y"];
	$x4 = $p4["x"]; $y4 = $p4["y"];

	$dx1 = $p2["x"] - $x1;
	$dx2 = $p3["x"] - $x4;

	$m1 = $dx1 ? ($p2["y"] - $y1) / $dx1 : log(0);
	$m2 = $dx2 ? ($p3["y"] - $y4) / $dx2 : log(0);

	$p = array();

	if (!$dx1) {

		$p["x"] = $x1;
		$p["y"] = $m2 * ($x1 - $x4) + $y4;
		// infinity

	}
	else if (!$dx2) {

		$p["x"] = $x4;
		$p["y"] = $m1 * ($x4 - $x1) + $y1;
		// infinity
	}
	else {

		$xInt = (-$m2 * $x4 + $y4 + $m1 * $x1 - $y1) / ($m1 - $m2);
		$yInt = $m1 * ($xInt - $x1) + $y1;
		$p["x"] = $xInt; $p["y"] = $yInt;
	}

	return $p;
}

function midLine ($a, $b) {

	$p = array();
	$p["x"] = ($a["x"] + $b["x"]) / 2;
	$p["y"] = ($a["y"] + $b["y"]) / 2;
	return $p;
}

function bezierSplit ($p0, $p1, $p2, $p3) {

	$p01 = midLine($p0, $p1);
	$p12 = midLine($p1, $p2);
	$p23 = midLine($p2, $p3);
	$p02 = midLine($p01, $p12);
	$p13 = midLine($p12, $p23);
	$p03 = midLine($p02, $p13);

	$b = array();
	$b["b0"] = array();
	$b["b1"] = array();

	$b["b0"]["a"] = $p0;
	$b["b0"]["b"] = $p01;
	$b["b0"]["c"] = $p02;
	$b["b0"]["d"] = $p03;

	$b["b1"]["a"] = $p03;
	$b["b1"]["b"] = $p13;
	$b["b1"]["c"] = $p23;
	$b["b1"]["d"] = $p3;

	return $b;
};

function mc_cBez($a, $b, $c, $d, &$mcp, $k) {

	global $kcount;
	// find intersection between bezier arms
	$s = intersect2Lines ($a, $b, $c, $d);

	// find distance between the midpoints
	$dx = ($a["x"] + $d["x"] + $s["x"] * 4 - ($b["x"] + $c["x"]) * 3) * .125;
	$dy = ($a["y"] + $d["y"] + $s["y"] * 4 - ($b["y"] + $c["y"]) * 3) * .125;

	$ap = $dx * $dx + $dy * $dy;

	//if($kcount++ > 200) die("too much iterations");
	// split curve if the quadratic isn't close enough
	if ($ap > $k) {

		$halves = bezierSplit ($a, $b, $c, $d);
		$b0 = $halves["b0"];
		$b1 = $halves["b1"];

		// recursive call to subdivide curve
		mc_cBez($a, $b0["b"], $b0["c"], $b0["d"], $mcp, $k);
		mc_cBez($b1["a"], $b1["b"], $b1["c"], $d, $mcp, $k);
	}
	else {

		// end recursion by drawing quadratic bezier

		$mi = count($mcp);

		$mcp[$mi] = array();
		$mcp[$mi]["cx"] = $s["x"];
		$mcp[$mi]["cy"] = $s["y"];
		$mcp[$mi]["ax"] = $d["x"];
		$mcp[$mi]["ay"] = $d["y"];

		//mc_curveTo($s["x"], $s["y"], $d["x"], $d["y"], $mcp);
	}
}

$kcount = 0;

function mc_drawBezierPts($p1, $p2, $p3, $p4, &$mcp, $tolerance = 5) {

	mc_cBez($p1, $p2, $p3, $p4, $mcp, $tolerance);
}

function mc_drawBezier ($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4, $tolerance = 5) {

	$mcp = array();
	$p1 = array();
	$p2 = array();
	$p3 = array();
	$p4 = array();

	$p1["x"] = $x1;
	$p1["y"] = $y1;

	$p2["x"] = $x2;
	$p2["y"] = $y2;

	$p3["x"] = $x3;
	$p3["y"] = $y3;

	$p4["x"] = $x4;
	$p4["y"] = $y4;

	mc_cBez($p1, $p2, $p3, $p4, $mcp, $tolerance * $tolerance);

	return $mcp;
}

function mc_curveToCubic ($x1, $y1, $x2, $y2, $x3, $y3, $mcp, $tolerance = 5) {

	$p1 = array();
	$p2 = array();
	$p3 = array();
	$p4 = array();

	$p1["x"] = $mcp["xpen"];  // it has to be defined
	$p1["y"] = $mcp["ypen"];

	$p2["x"] = $x1;
	$p2["y"] = $y1;

	$p3["x"] = $x2;
	$p3["y"] = $y2;

	$p4["x"] = $x3;
	$p4["y"] = $y3;

	mc_cBez($p1, $p2, $p3, $p4, $mcp, $tolerance * $tolerance);
}

function mc_curveToCubicPts ($p1, $p2, $p3, $mcp, $tolerance = 5) {

	$p0 = array();

	$p0["x"] = $mcp["xpen"];
	$p0["y"] = $mcp["ypen"];

	mc_cBez ($p0, $p1, $p2, $p3, $mcp, $tolerance * $tolerance );
};



////////////////////////////////////////////////////////////////////////
function drawArc($x, $y, $radius, $arcSweep, $startAngle, $yRadius) {
	// ==============
	// mc.drawArc() - by Ric Ewing (ric@formequalsfunction.com) - version 1.5 - 4.7.2002
	//
	// x, y = This must be the current pen position... other values will look bad
	// radius = radius of Arc. If [optional] yRadius is defined, then r is the x radius
	// arc = sweep of the arc. Negative values draw clockwise.
	// startAngle = starting angle in degrees.
	// yRadius = [optional] y radius of arc. Thanks to Robert Penner for the idea.
	// ==============
	// Thanks to: Robert Penner, Eric Mueller and Michael Hurwicz for their contributions.
	// ==============

	// if yRadius is undefined, yRadius = radius
	if (!isset($yRadius)) $yRadius = $radius;

	// Init vars
	//$segAngle, $theta, $angle, $angleMid, $segs, $ax, $ay, $bx, $by, $cx, $cy;

	// no sense in drawing more than is needed :)
	if (abs($arcSweep) > 360) $arc = 360;


	$mcp = array();

	// Flash uses 8 segments per circle, to match that, we draw in a maximum
	// of 45 degree segments. First we calculate how many segments are needed
	// for our arc.
	$segs = ceil(abs($arcSweep) / 45);

	// Now calculate the sweep of each segment
	$segAngle = $arcSweep / $segs;

	// The math requires radians rather than degrees. To convert from degrees
	// use the formula (degrees/180)*Math.PI to get radians.
	$theta = -($segAngle / 180) * pi();

	// convert angle startAngle to radians
	$angle = -($startAngle / 180) * pi();

	// find our starting points (ax,ay) relative to the secified x,y
	$ax = $x - cos($angle) * $radius;
	$ay = $y - sin($angle) * $yRadius;

	// if our arc is larger than 45 degrees, draw as 45 degree segments
	// so that we match Flash's native circle routines.
	if ($segs > 0) {
		// Loop for drawing arc segments
		for ($i = 0; $i < $segs; $i++) {

			$mi = count($mcp);
			$mcp[$mi] = array();
			// increment our angle
			$angle += $theta;

			// find the angle halfway between the last angle and the new
			$angleMid = $angle - ($theta / 2);

			// calculate our end point
			$bx = $ax + cos($angle) * $radius;
			$by = $ay + sin($angle) * $yRadius;

			// calculate our control point
			$cx = $ax + cos($angleMid) * ($radius / cos($theta / 2));
			$cy = $ay + sin($angleMid) * ($yRadius / cos($theta / 2));

			// draw the arc segment
			/* TO DO: MANAGE THIS */
			//this.curveTo($cx, $cy, $bx, $by);
			$mcp[$mi]["cx"] = $cx;
			$mcp[$mi]["cy"] = $cy;
			$mcp[$mi]["ax"] = $bx;
			$mcp[$mi]["ay"] = $by;
			/* ------------------------ */
		}
	}

	return $mcp;
};

////////////////////////////////////////////////////////////////////////
// this group of functions it's not swf specific
// it provides general drawing support
// such point translation, rotation and so on
////////////////////////////////////////////////////////////////////////


// rotates a point around another
// positive angle rotate ccw
function point_rotate($x, $y, $centerX = 0, $centerY = 0, $angle = 45){

	$p = array();
	$r = deg2rad($angle);
	$ipo = sqrt($x * $x + $y * $y);

	//$x2 = cos($r) * $x1 - sin($r) * $y1;
	//$y2 = sin($r) * $x1 + cos($r) * $y1;
	$x1 = $ipo * cos($r);
	$y1 = $ipo * sin($r);

	return point_translate($x1, $y1, $centerX, $centerY);
}

// translates a point of a given amount x,y
function point_translate($x, $y, $translateX = 0, $translateY = 0){

	$p = array();
	$p["x"] = $x + $translateX;
	$p["y"] = $y + $translateY;

	return $p;
}

// scale point from a given coord
function point_scale($x, $y, $scaleX = 1, $scaleY = 1, $centerX = 0, $centerY = 0){

	$p = array();

	$x1 = $x - $centerX;
	$y1 = $y - $centerY;

	$x2 = $x1 * $scaleX;
	$y2 = $y1 * $scaleY;

	$p["x"] = $x2 + $centerX;
	$p["y"] = $y2 + $centerY;

	return $p;
}

// returns a number from a given array
// > 0 means "clockwise"
// < 0 means "counter-clockwise"
// the flag findBoundary set the call to the find_boundary
function find_drawing_direction($coordAr, $findBoundary = true){

	$sum = 0; // clockwise by default

	if(count($coordAr) <= 2) return $sum; // lines will be considered clockwise

	if($findBoundary){
		$boundary = array();
		$boundary = find_boundary($coordAr);

		for($i = 0;  $i < 3; $i++)
			$sum += $boundary[$i]["idx"] >= $boundary[$i + 1]["idx"] ? 1 : -1;
	}
	else{
		for($i = 0;  $i < 3; $i++)
			$sum += $coordAr[$i]["idx"] >= $coordAr[$i + 1]["idx"] ? 1 : -1;
	}

	return $sum;
}

// finds the boundary of a given shape
// with coords stored in an associative array
// returns an array containing for items
// each items contains these fields
// coord, idx
function find_boundary($coords){

	$boundary = array();
	$boundary[0]["coord"] = 1000000; // xmin
	$boundary[0]["idx"] = -1; //

	$boundary[1]["coord"] = -1000000; // ymax
	$boundary[1]["idx"] = -1; //

	$boundary[2]["coord"] = -1000000; // xmax
	$boundary[2]["idx"] = -1; //

	$boundary[3]["coord"] = 1000000; // ymin
	$boundary[3]["idx"] = -1; //

	$cLength = count($coords);

	for($i = 0; $i < $cLength; $i++){

		$c = $coords[$i];
		$cl = count($c);

		if($cl >= 4){
			$x = $c["ax"];
			$y = $c["ay"];
		}
		else {
			$x = $c["x"];
			$y = $c["y"];
		}

		if($x < $boundary[0]["coord"]){
			$boundary[0]["coord"] = $x;
			$boundary[0]["idx"] = $i;
		}

		if($x > $boundary[2]["coord"]){
			$boundary[2]["coord"] = $x;
			$boundary[2]["idx"] = $i;
		}

		if($y < $boundary[3]["coord"]){
			$boundary[3]["coord"] = $y;
			$boundary[3]["idx"] = $i;
		}

		if($y > $boundary[1]["coord"]){
			$boundary[1]["coord"] = $y;
			$boundary[1]["idx"] = $i;
		}
	}

	return $boundary;
}


// returns true | false if slaveBoundary is into a masterBoundary
// e.g. when detecting an "hole"
function is_in_boundary($masterBoundary, $slaveBoundary){

	$inBoundary = false;

	if($masterBoundary[0]["coord"] > $slaveBoundary[0]["coord"]) return false; // xmin exceeds
	if($masterBoundary[1]["coord"] < $slaveBoundary[1]["coord"]) return false; // ymax exceeds
	if($masterBoundary[2]["coord"] < $slaveBoundary[2]["coord"]) return false; // xmax exceeds
	if($masterBoundary[3]["coord"] > $slaveBoundary[3]["coord"]) return false; // ymin exceeds

	return true; // masterBoundary surrounds slaveBoundary
}

// reverse path
function reverse_coords ($coords){

	$cLength = count($coords) - 1;

	$revCoords = array();
	$anchors = array();

	if(count($coords[$cLength]) > 2) {
		$revCoords[0]["x"] = $coords[$cLength]["ax"];
		$revCoords[0]["y"] = $coords[$cLength]["ay"];
	}


	foreach($coords as $c){

		$ri = count($anchors);

		if(count($c) > 2){
			$anchors[$ri]["x"] = $c["ax"];
			$anchors[$ri]["y"] = $c["ay"];
		}
		else{
			$anchors[$ri]["x"] = $c["x"];
			$anchors[$ri]["y"] = $c["y"];
		}
	}

	for($i = $cLength; $i >= 0; $i--){

		$ri = count($revCoords);
		$c = $coords[$i];
		$cCount = count($c);

		if($cCount > 2){

			$revCoords[$ri]["ax"] = $anchors[$i - 1]["x"];
			$revCoords[$ri]["ay"] = $anchors[$i - 1]["y"];

			if($cCount == 4){

				$revCoords[$ri]["cx"] = $c["cx"];
				$revCoords[$ri]["cy"] = $c["cy"];
			}
			else{

				$revCoords[$ri]["c1x"] = $c["c2x"];
				$revCoords[$ri]["c1y"] = $c["c2y"];
				$revCoords[$ri]["c2x"] = $c["c1x"];
				$revCoords[$ri]["c2y"] = $c["c1y"];
			}
		}
		else {
			$revCoords[$ri]["x"] = $anchors[$i]["x"];
			$revCoords[$ri]["y"] = $anchors[$i]["y"];
		}
	}

	return $revCoords;
}

// build an array with "star" coords
function get_coords_star($centerX, $centerY, $radiusExt = 0, $radiusInt = 0, $spikes = 5, $rotation = 0){

	$k = 0.38;
	$starCoords = array();

	if(!$radiusInt && !$radiusExt){
		$radiusExt = 100;
		$radiusInt = $radiusExt * $k;
	}
	if(!$radiusInt || $radiusInt == $radiusExt)
		$radiusInt = $radiusExt * $k;

	if($radiusInt == "auto")
		$radiusInt = $radiusExt * $k;

	if($radiusInt > $radiusExt){
		$tmpRadius = $radiusInt;
		$radiusExt = $radiusInt;
		$radiusInt = $tmpRadius;
	}

	$spikeAngle = 360 / $spikes;
	$gapAngle = $spikeAngle / 2;
	$spikeRotation = $rotation - 90;

	$x = cos(deg2rad($spikeRotation)) * $radiusExt;
	$y = sin(deg2rad($spikeRotation)) * $radiusExt;

	for($i = 0; $i < $spikes; $i++){

		$x = cos(deg2rad($spikeRotation)) * $radiusExt;
		$y = sin(deg2rad($spikeRotation)) * $radiusExt;

		$sidx = count($starCoords);

		$starCoords[$sidx]["x"] = $x;
		$starCoords[$sidx]["y"] = $y;

		$spikeRotation += $gapAngle;

		$x = cos(deg2rad($spikeRotation)) * $radiusInt;
		$y = sin(deg2rad($spikeRotation)) * $radiusInt;

		$starCoords[$sidx + 1]["x"] = $x;
		$starCoords[$sidx + 1]["y"] = $y;

		$spikeRotation += $gapAngle;
	}

	$sidx = count($starCoords);
	$starCoords[$sidx]["x"] = $starCoords[0]["x"];
	$starCoords[$sidx]["y"] = $starCoords[0]["y"];

	$sc = array();
	$j = 0;
	foreach($starCoords as $s){
		$sc[$j] = point_translate($s["x"], $s["y"], $centerX, $centerY);
		$j++;
	}

	return $sc;
}

// build an array with "polygon" coords
function get_coords_polygon($centerX, $centerY, $radius = 100, $sides = 3, $rotation = 0){

	$pc = array();

	$sideAngle = 360 / $sides;
	$sideRotation = $rotation - 90;

	for($i = 0; $i < $sides; $i++){

		$x = cos(deg2rad($sideRotation)) * $radius;
		$y = sin(deg2rad($sideRotation)) * $radius;

		$pidx = count($pc);

		$pc[$pidx] = point_translate($x, $y, $centerX, $centerY);

		$sideRotation += $sideAngle;
	}

	$pidx = count($pc);
	$pc[$pidx]["x"] = $pc[0]["x"];
	$pc[$pidx]["y"] = $pc[0]["y"];

	return $pc;
}

/**
 *
 */
function parse_color($cStr){

	$color = $cStr;

	if(strpos($color, "url") !== false){
		$urlID = substr($color, strpos($color, "#") + 1, -1);
		return $urlID;
	}

	if(strpos(strtolower($color), "rgb") !== false){

		$color = substr($color, strpos($color, "(") + 1);
		$color = subStr($color, 0, strpos($color, ")"));

		$cAr = split(",", $color);
		$color  = "#" .str_pad(dechex($cAr[0]), 2, "0", STR_PAD_LEFT);
		$color .= str_pad(dechex($cAr[1]), 2, "0", STR_PAD_LEFT);
		$color .= str_pad(dechex($cAr[2]), 2, "0", STR_PAD_LEFT);
	}
	else $color = get_svgColorValue($color);

	$cLength = strlen($color);

	if($cLength == 4){
		$r = str_repeat($color[1], 2);
		$g = str_repeat($color[2], 2);
		$b = str_repeat($color[3], 2);
		$color = "#" .$r. $g. $b;
	}
	else if($cLength < 7) $color .=  str_repeat("0", 7 - $cLength);

	return get_color_array($color, 0xff, true);
}

// ---------------------------- end of mk lib draw -------------
?>
